package org.dromara.exam.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.utils.QueueUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.exam.domain.dto.*;
import org.dromara.exam.domain.entity.ExamAnswer;
import org.dromara.exam.domain.bo.ExamAnswerBo;
import org.dromara.exam.domain.vo.ExamAnswerVo;
import org.dromara.exam.domain.vo.ExamProjectVo;
import org.dromara.exam.mapper.ExamAnswerMapper;
import org.dromara.exam.service.IExamAnswerService;
import org.dromara.exam.service.IExamRepoService;
import org.dromara.exam.util.AnswerScoreEvaluator;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 答题Service业务层处理
 *
 * @author xiyin321
 * @date 2024-05-24
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class ExamAnswerServiceImpl implements IExamAnswerService {

    private final ExamAnswerMapper baseMapper;
    private final IExamRepoService repoService;

    /**
     * 查询答题
     */
    @Override
    public ExamAnswerVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询答题分页列表
     */
    @Override
    public TableDataInfo<ExamAnswerVo> queryPage(ExamAnswerBo bo, PageQuery pageQuery) {
        Page<ExamAnswerVo> result = baseMapper.selectAnswerPage(pageQuery.build(), this.buildAnswerQueryWrapper(bo));
        return TableDataInfo.build(result);
    }

    private Wrapper<ExamAnswer> buildAnswerQueryWrapper(ExamAnswerBo bo) {
        QueryWrapper<ExamAnswer> wrapper = Wrappers.query();
        wrapper
            .eq("a.del_flag", "0")
            .eq(ObjectUtil.isNotNull(bo.getProjectId()), "a.project_id", bo.getProjectId())
            .eq(ObjectUtil.isNotNull(bo.getTempSave()), "a.temp_save", bo.getTempSave())
            .eq(ObjectUtil.isNotNull(bo.getCreateBy()), "a.create_by", bo.getCreateBy());
//            .eq(ObjectUtil.isNotNull(bo.getExamExerciseType()), "a.exam_exercise_type", bo.getExamExerciseType())

        return wrapper;
    }

    /**
     * 查询当前用户在指定项目下的答卷列表
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public List<ExamAnswerVo> queryProjectListByUserId(ExamAnswerBo bo) {
        return baseMapper.selectVoList(Wrappers.<ExamAnswer>lambdaQuery()
            .eq(ExamAnswer::getDelFlag, "0")
            .eq(ObjectUtil.isNotNull(bo.getProjectId()) ,ExamAnswer::getProjectId, bo.getProjectId())
            .eq(ExamAnswer::getCreateBy, LoginHelper.getUserId()));
    }

    /*查询排名列表*/
    @Override
    public TableDataInfo<ExamAnswerVo> queryRankPageList(ExamAnswerBo bo, PageQuery pageQuery) {
        Page<ExamAnswerVo> result = baseMapper.selectAnswerPage(pageQuery.build(), this.buildAnswerRankQueryWrapper(bo));
        int currentOrder = 1;
        int previousOrder = 1;
        Double previousScore = null;
        /*遍历列表，添加排名信息*/
        for (ExamAnswerVo item : result.getRecords()) {
            Double currentScore = item.getExamScore();
            if (previousScore != null && !currentScore.equals(previousScore)) {
                currentOrder = previousOrder + 1;
            }
            item.setOrderNum(currentOrder);
            previousOrder = currentOrder;
            previousScore = currentScore;
        }
        return TableDataInfo.build(result);
    }

    private Wrapper<ExamAnswer> buildAnswerRankQueryWrapper(ExamAnswerBo bo) {
        QueryWrapper<ExamAnswer> wrapper = Wrappers.query();
        wrapper
            .eq("a.del_flag", "0")
            .eq(ObjectUtil.isNotNull(bo.getProjectId()), "a.paper_id", bo.getProjectId())
            .orderByDesc("a.exam_score");
        return wrapper;
    }


    /*根据项目ID列表查询答卷列表*/
    @Override
    public List<ExamAnswerVo> queryPapersList(ExamAnswerBo bo) {
//        if (bo.getPapersList() == null || bo.getPapersList().isEmpty()) {
//            return null;
//        }
        return baseMapper.selectVoList(Wrappers.<ExamAnswer>lambdaQuery()
            .eq(ExamAnswer::getDelFlag, "0"));
//            .in(ExamAnswer::getProjectId, bo.getPapersList()));
    }

    /**
     * 根据项目ID列表查询答卷数量
     *
     * @param ids 项目ID列表
     * @return
     */
    @Override
    public List<ExamProjectVo> queryAnswerCountByPaperIds(List<Long> ids) {
        return baseMapper.queryVoCounts(ids);
    }

    /**
     * 根据项目ID查询未提交答卷
     *
     * @param id
     */
    @Override
    public void submitAnswerByProjectId(Long id) {
        // 1. 查询未提交的答卷列表
        List<ExamAnswer> answerList = baseMapper.selectList(Wrappers.<ExamAnswer>lambdaQuery()
            .eq(ExamAnswer::getProjectId, id)
            .ne(ExamAnswer::getTempSave, "1")
            .eq(ExamAnswer::getDelFlag, "0"));

        // 2. 批量提交到分数计算队列
        answerList.forEach(answer -> {
            // 转换为计算需要的BO
            ExamAnswerBo calculateBo = BeanUtil.copyProperties(answer, ExamAnswerBo.class);

            // 生成唯一任务ID
            String taskId = "BATCH_SCORE_TASK_" + IdUtil.getSnowflakeNextIdStr();

            // 创建计算任务对象
            ScoreCalculatorTask task = new ScoreCalculatorTask(
                taskId,
                answer.getCreateBy(), // 从答卷中获取用户ID
                calculateBo
            );

            // 提交到队列
            QueueUtils.addQueueObject("exam:score:queue", task);

            // 设置初始状态
            RedisUtils.setCacheObject(
                "exam:score:status:" + taskId,
                "processing",
                Duration.ofMinutes(300)
            );

            log.info("已提交分数计算任务，answerId: {}, taskId: {}", answer.getId(), taskId);
        });
    }

    /**
     * 查询答卷列表
     */
    @Override
    public List<ExamAnswerVo> queryList(ExamAnswerBo bo) {
        LambdaQueryWrapper<ExamAnswer> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<ExamAnswer> buildQueryWrapper(ExamAnswerBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<ExamAnswer> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getProjectId() != null, ExamAnswer::getProjectId, bo.getProjectId());
        lqw.eq(ObjectUtil.isNotNull(bo.getTempSave()), ExamAnswer::getTempSave, bo.getTempSave());
//        lqw.eq(ObjectUtil.isNotNull(bo.getExamExerciseType()), ExamAnswer::getExamExerciseType, bo.getExamExerciseType());
        return lqw;
    }

    /**
     * 新增答卷
     */
    @Override
    public ExamAnswerVo insertByBo(ExamProjectVo bo) {
        ExamAnswerVo vo = replaceSchemaIfRandomSchema(bo);
        AnswerMetaInfo metaInfo = new AnswerMetaInfo();
        AnswerMetaInfo.ClientInfo clientInfo = new AnswerMetaInfo.ClientInfo();
        clientInfo.setRemoteIp(ServletUtils.getClientIP());
        clientInfo.setCookie(ServletUtils.getRequest().getHeader("Cookie"));
        clientInfo.setAgent(ServletUtils.getRequest().getHeader("User-Agent"));
        metaInfo.setClientInfo(clientInfo);
        vo.setMetaInfo(metaInfo);
        return vo;
    }

    // 随机问题
    private ExamAnswerVo replaceSchemaIfRandomSchema(ExamProjectVo project) {
        TemplateSchema source = project.getSurvey();
        // 获取随机问题配置
        List<ProjectSetting.RandomQuestionCondition> randomSurveyCondition = project
            .getSetting().getExamSetting().getRandomProject();

        // 当随机条件为空时直接处理编辑器题目
        List<TemplateSchema> templateSchemaList;
        if (randomSurveyCondition == null || randomSurveyCondition.isEmpty()) {
            // 直接使用编辑器题目
            if (source == null || CollectionUtils.isEmpty(source.getChildren())) {
                // 无题目时返回null
                return null;
            }
            templateSchemaList = new ArrayList<>(source.getChildren());
        } else {
            // 从题库抽取题目
            List<TemplateSchema> pickedTemplates = repoService.pickTemplateFromRepo(randomSurveyCondition);

            // 合并逻辑仅在题库有题目时执行
            if (!CollectionUtils.isEmpty(pickedTemplates) && source != null && !CollectionUtils.isEmpty(source.getChildren())) {
                templateSchemaList = Stream.concat(
                        source.getChildren().stream(),
                        pickedTemplates.stream())
                    .sorted(Comparator.comparing(TemplateSchema::getOrderNum))
                    .collect(Collectors.toList());
            } else {
                templateSchemaList = pickedTemplates; // 仅保留题库题目
            }
        }

        // 排序、随机顺序处理等
        if (project.getSetting().getExamSetting().getRandomOrder() != null
            && project.getSetting().getExamSetting().getRandomOrder()) {
            randomSchemaOrder(templateSchemaList);
        }

        TemplateSchema randomSchema = TemplateSchema.builder()
            .id(source.getId())
            .children(templateSchemaList)
            .title(source.getTitle())
            .attribute(source.getAttribute())
            .description(source.getDescription())
            .build();

        if (!templateSchemaList.isEmpty()) {
            ExamAnswer answer = new ExamAnswer();
            answer.setSurvey(randomSchema);
            answer.setProjectId(project.getId());
            baseMapper.insert(answer);
            return this.queryById(answer.getId());
        }
        return null;
    }

    // 随机选项顺序
    private void randomSchemaOrder(List<TemplateSchema> list) {
        list.forEach(item -> {
            if (item.getType().equals(TemplateSchema.QuestionType.Radio) || item.getType().equals(TemplateSchema.QuestionType.Checkbox)) {
                Collections.shuffle(item.getChildren());
            }
        });
    }

    /*批量添加*/
    @Override
    public void batchInsertOrUpdate(Collection<ExamAnswer> answers) {
        baseMapper.insertOrUpdateBatch(answers);
    }

    /**
     * 暂存答卷
     *
     * @param bo
     * @return
     */
    @Override
    public Boolean tempSaveByBo(ExamAnswerBo bo) {
        bo.setTempSave("0");
        ExamAnswer update = MapstructUtils.convert(bo, ExamAnswer.class);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存答卷
     */
    @Override
    public ExamAnswerVo updateByBo(ExamAnswerBo bo) {
        // 计算分数
        beforeSaveAnswer(bo);
        bo.setTempSave("1");
        bo.getMetaInfo().getAnswerInfo().setEndTime(new Date());
        ExamAnswer update = MapstructUtils.convert(bo, ExamAnswer.class);
        validEntityBeforeSave(update);
        boolean flag = baseMapper.updateById(update) > 0;
        if (flag) {
            return MapstructUtils.convert(update, ExamAnswerVo.class);
        }
        return null;
    }

    // 保存前处理
    private void beforeSaveAnswer(ExamAnswerBo bo) {
        AnswerScoreEvaluator evaluator = new AnswerScoreEvaluator(bo.getSurvey(), bo.getAnswer());
        bo.setExamScore(evaluator.eval());
        AnswerExamInfo examInfo = new AnswerExamInfo();
        examInfo.setQuestionScore(evaluator.getQuestionScore());
        bo.setExamInfo(examInfo);
    }


    /*修改答卷分数*/
    @Override
    public ExamAnswerVo updateScoreById(ExamAnswerBo bo) {
        /*计算总分*/
        double score = 0;
        for (Double s : bo.getExamInfo().getQuestionScore().values()) {
            score += s;
        }
        bo.setExamScore(score);
        ExamAnswer update = MapstructUtils.convert(bo, ExamAnswer.class);
        baseMapper.updateById(update);
        return this.queryById(bo.getId());
    }


    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ExamAnswer entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除答题
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /*根据项目ID进行删除*/
    @Override
    public Boolean deleteWithValidByPaperIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchPaperIds(ids) > 0;
    }

    /*获取项目的案卷数量*/
    @Override
    public Long getAnswerCountByPaperId(Long id) {
        return baseMapper.selectCount(new LambdaQueryWrapper<ExamAnswer>()
            .eq(ExamAnswer::getDelFlag, "0")
            .eq(ExamAnswer::getProjectId, id));
    }


}
